
float getCorrectedLinearZ(vec2 uv, vec3 view)
{
  float z = GetZ(uv);
#ifdef SSR_GroundCorrection
  if ( z < 0.0001 || z > 0.9999 )
  {
    z = GetViewRayPlaneInter(uv, view, cb_GroundPlane.ViewEquation).z;
    z = (z + cb_PostEffectAuto.u_NearFar.x) / (cb_PostEffectAuto.u_NearFar.x - cb_PostEffectAuto.u_NearFar.y);
  }
  else
#endif
  {
    z = LinearizeZ(z);
  }
  return z;
}

void main()
{
  //FRAGCOLOR = vTexture(ReflRadianceMap, vTexCoord.xy);
  //return;

  vec3 normalRef = GetNormal(vTexCoord.xy).xyz;
	if (vDot(normalRef, normalRef) < 0.01)
  {
    FRAGCOLOR = vec4(0.0);
    return;
  }

  const vec3 viewVec = UVToViewVect(vTexCoord.xy); // Simplified view vector
  float depthRef = getCorrectedLinearZ(vTexCoord.xy, viewVec);

  vec4 f0RoughRef = GetEffectTexture(F0RoughnessMap, vTexCoord.xy);
  float roughness = f0RoughRef.a;

  vec4 color = vec4(0.0);
  float totalWeight = 0.0;

  float NoV = vDot(normalRef, viewVec);
  float oneMinusNoV = 1.0 - vAbs(NoV);

  vec3 tangent = vCross(normalRef, viewVec);
  float kernelSizeFactor = vSqrt(roughness);
  int kernelSize = int(kernelSizeFactor * float(cb_PostEffect_SSR_Blur.u_MaxKernelSize));

#ifdef DISK_BLUR
  float maxDistSqr = float((kernelSize+1)*(kernelSize+1));
#endif

  for (int i = -kernelSize; i <= kernelSize+1; i++)
  {
#ifdef DISK_BLUR
  for (int j = -kernelSize; j <= kernelSize+1; j++)
  {
    float distSqr = float(i*i+j*j);
    if ( distSqr > maxDistSqr )
      continue;
    float dist = vSqrt(distSqr);
    vec2 uvs = vTexCoord.xy + vec2(i*cb_PostEffect_SSR_Blur.u_ReflMapPixelSize.x, j*cb_PostEffect_SSR_Blur.u_ReflMapPixelSize.y);
    vec2 uvsNext = uvs + vec2(vSign(i)*cb_PostEffect_SSR_Blur.u_ReflMapPixelSize.x, vSign(j)*cb_PostEffect_SSR_Blur.u_ReflMapPixelSize.y);
#else
    float dist = float(vAbs(i));
#ifdef HORIZONTAL_BLUR
    vec2 uvs = vTexCoord.xy + vec2(i*cb_PostEffect_SSR_Blur.u_ReflMapPixelSize.x, 0.0);
    vec2 uvsNext = uvs + vec2(vSign(i)*cb_PostEffect_SSR_Blur.u_ReflMapPixelSize.x, 0.0);
#else
    vec2 uvs = vTexCoord.xy + vec2(0.0, i*cb_PostEffect_SSR_Blur.u_ReflMapPixelSize.y);
    vec2 uvsNext = uvs + vec2(0.0, vSign(i)*cb_PostEffect_SSR_Blur.u_ReflMapPixelSize.y);
#endif
#endif
    float distRatio = 1.0 - dist / float(kernelSize+1);

    vec3 normal = GetNormal(uvs).xyz;
    // "1.01" factor avoids normal float approximation errors (those errors could make a strong light go through an object on perfect mirrors)
    float normalRatio = vSaturate(1.01 * vDot(normal, normalRef));
    
    float depth = getCorrectedLinearZ(uvs, UVToViewVect(uvs));
    float depthRatio = vAbs(1.0 - depth / depthRef);
    depthRatio = 1.0 - vSaturate(oneMinusNoV * cb_PostEffect_SSR_Blur.u_DepthDiscontinuity * depthRatio * dist);

    vec3 f0 = GetEffectTexture(F0RoughnessMap, uvsNext).xyz;
    vec3 f0Diff = f0RoughRef.xyz - f0;
    float f0Ratio = vSaturate(1.0 - cb_PostEffect_SSR_Blur.u_MaterialDiscontinuity * vDot(f0Diff, f0Diff));

    float weightRatio = distRatio * f0Ratio;
    float globalRatio = weightRatio * normalRatio * depthRatio;
    vec4 reflSample = vTexture(ReflRadianceMap, uvs);
    color += vec4(reflSample.xyz*reflSample.a, reflSample.a) * globalRatio;
    totalWeight += weightRatio;
  }
#ifdef DISK_BLUR
  } // "j for" loop end
#endif

  if ( color.a > 0.0 )
  {
    FRAGCOLOR.rgb = color.rgb / color.a;
    FRAGCOLOR.a = color.a / totalWeight;
  }
  else
    FRAGCOLOR = vec4(0.0);
}
